package com.cl.mvc.service;

import com.cl.mvc.entity.BaseEntity;
import com.cl.mvc.entity.JDBaseEntity;
import com.cl.mvc.repository.BaseRepository;
import com.cl.mvc.repository.JDBaseRepository;
import com.cl.mvc.util.FieldUtil;
import com.cl.mvc.util.UpdateUtil;
import com.jandar.pile.common.util.JwtUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.util.*;

public abstract class JDBaseService<Entity extends JDBaseEntity> {
    protected final JDBaseRepository<Entity> repository;
    @Autowired
    public JwtUtil<Long, Long> jwtUtil;

    public JDBaseService(JDBaseRepository<Entity> repository) {
        this.repository = repository;
    }

    public List<Entity> findByField(String field, String value) {
        return repository.findAll((Specification<Entity>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            predicateList.add(criteriaBuilder.equal(root.get(field), value));
            return criteriaQuery.where(predicateList.toArray(new Predicate[0])).getRestriction();
        });
    }

    public Entity saveIfNotExists(Entity entity, String auth) {
        Optional<Entity> exist = findByEntity(entity);
        if (exist.isPresent()) {
            return save(exist.get(), auth);
        } else {
            return save(entity, auth);
        }
    }

    public Entity saveIfNotExists(Entity entity) {
        Optional<Entity> exist = findByEntity(entity);
        return exist.orElseGet(() -> repository.save(entity));
    }

    public Entity save(Entity entity) {
        entity.setPower(cleanData(entity.getPower()));
        if (entity.getId() != null) {
            Entity target = findById(entity.getId()).get();
            UpdateUtil.copyNullProperties(entity, target);
            return repository.save(target);
        }
        return repository.save(entity);
    }

    public int saveAll(List<Entity> entityList, String auth) {
        String userId = String.valueOf(jwtUtil.getUserId(auth));
        String showName = jwtUtil.getShowName(auth);
        String systemCode = jwtUtil.getSystemCode(auth);
        entityList.forEach(entity -> {
            entity.setPower(cleanData(entity.getPower()));
            if (StringUtils.isNotBlank(userId)) {
                if (entity.getId() == null) {
                    entity.setCreatedBy(userId);
                    entity.setCreatedByShowName(showName);
                }
                entity.setUpdatedBy(userId);
                entity.setUpdatedByShowName(showName);
                entity.setSystemCode(systemCode);
            }
        });
        return repository.saveAll(entityList).size();
    }

    public Entity save(Entity entity, String auth) {
        String userId = String.valueOf(jwtUtil.getUserId(auth));
        String showName = jwtUtil.getShowName(auth);
        String systemCode = jwtUtil.getSystemCode(auth);
        if (StringUtils.isNotBlank(userId)) {
            if (entity.getId() == null) {
                entity.setCreatedBy(userId);
                entity.setCreatedByShowName(showName);
                entity.setSystemCode(systemCode);
            }
            entity.setUpdatedBy(userId);
            entity.setUpdatedByShowName(showName);
            if (StringUtils.isBlank(entity.getPower())) {
                entity.setPower("r_" + userId + ",");
            } else if (!entity.getPower().contains("r_" + userId + ",")) {
                entity.setPower(entity.getPower() + ",r_" + userId + ",");
            }
        }
        return save(entity);
    }

    public List<Entity> findAll(Specification<Entity> specification) {
        return repository.findAll(specification);
    }

    public List<Entity> findAll() {
        return repository.findAll();
    }

    public List<Entity> findAll(Entity entity) {
        return repository.findAll(buildSpecification(entity));
    }

    public Integer saveAllPartition(List<Entity> entities, Integer batchSize) {
        int res = 0;
        for (int i = 0; i < entities.size(); i += batchSize) {
            int end = i + batchSize;
            if (end > entities.size()) {
                end = entities.size();
            }
            res += saveAll(entities.subList(i, end));
        }
        return res;

    }

    public Integer saveAllPartition(List<Entity> entities, Integer batchSize, String auth) {
        int res = 0;
        if (entities == null) {
            return 0;
        }
        for (int i = 0; i < entities.size(); i += batchSize) {
            int end = i + batchSize;
            if (end > entities.size()) {
                end = entities.size();
            }
            res += saveAll(entities.subList(i, end), auth);
        }
        return res;
    }

    public Integer saveAll(List<Entity> entities) {
        return repository.saveAll(entities).size();
    }


    public void deleteById(Integer id) {
        repository.deleteById(id);
    }

    public void deleteAllById(List<Integer> ids) {
        repository.deleteAllById(ids);
    }

    public void delete(Entity entity) {
        Optional<Entity> o = findByEntity(entity);
        o.ifPresent(value -> deleteById(value.getId()));
    }

    public void deleteAll(Entity entity) {
        List<Entity> list = findAllByEntity(entity);
        repository.deleteAll(list);
    }


    public List<Entity> list() {
        return repository.findAll();
    }


    public List<Entity> findAll(Entity entity, String auth) {
        return repository.findAll(buildSpecification(entity).and(buildPowerSpecification(auth)));
    }

    public List<Entity> listBy(Entity entity, String auth) {
        return repository.findAll(buildSpecification(entity).and(buildPowerSpecification(auth)));
    }
    public Page<Entity> page(Entity entity, String auth) {
        return repository.findAll(buildSpecification(entity).and(buildPowerSpecification(auth)), PageRequest.of(entity.getPageNumber() - 1, entity.getPageSize()));
    }


    public Specification<Entity> buildSpecification(Entity entity) {
        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            List<Order> orderList = new ArrayList<>();
            handleEntityParam(predicateList, orderList, criteriaBuilder, root, entity);
            if (orderList.isEmpty()) {
                orderList.add(criteriaBuilder.desc(root.get("id")));
            }
            return criteriaQuery.orderBy(orderList.toArray(new Order[0])).where(predicateList.toArray(new Predicate[0])).getRestriction();
        };
    }

    public Specification<Entity> buildSpecification(Entity entity, List<Order> orderList) {
        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            handleEntityParam(predicateList, orderList, criteriaBuilder, root, entity);
            if (orderList.isEmpty()) {
                orderList.add(criteriaBuilder.desc(root.get("id")));
            }
            return criteriaQuery.orderBy(orderList.toArray(new Order[0])).where(predicateList.toArray(new Predicate[0])).getRestriction();
        };
    }

    public Specification<Entity> buildPowerSpecification(String auth) {
        String userId = String.valueOf(jwtUtil.getUserId(auth));
        String roleId = String.valueOf(jwtUtil.getRoleId(auth));
        String depId = String.valueOf(jwtUtil.getDepId(auth));
        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            predicateList.add(criteriaBuilder.or(criteriaBuilder.equal(root.get("createdBy"), userId),
                    criteriaBuilder.equal(root.get("updatedBy"), userId),
                    criteriaBuilder.like(criteriaBuilder.concat(",", criteriaBuilder.concat(root.get("power"), ",")), "%,r_" + userId + ",%"),
                    criteriaBuilder.like(criteriaBuilder.concat(",", criteriaBuilder.concat(root.get("power"), ",")), "%,j_" + roleId + ",%"),
                    criteriaBuilder.like(criteriaBuilder.concat(",", criteriaBuilder.concat(root.get("power"), ",")), "%,b_" + depId + ",%"),
                    criteriaBuilder.and(
                            criteriaBuilder.or(criteriaBuilder.isNull(root.get("createdBy")), criteriaBuilder.equal(root.get("createdBy"), "null")),
                            criteriaBuilder.or(criteriaBuilder.equal(root.get("power"), ""), criteriaBuilder.isNull(root.get("power"))))
            ));
            return criteriaQuery.where(predicateList.toArray(new Predicate[0])).getRestriction();
        };
    }


    public void handleEntityParam(List<Predicate> predicateList, List<Order> orderList, CriteriaBuilder criteriaBuilder, Path root, Object entity) {
        List<Map<String, Object>> fieldList = FieldUtil.getFiledsInfo(entity);
        fieldList.stream().filter(field -> field.get("value") != null).forEach(field -> {
            String name = field.get("name").toString();
            Object value = field.get("value");
            Class<?> type = (Class<?>) field.get("type");
            if (!Collection.class.isAssignableFrom(type) && !type.cast(value).toString().isEmpty()) {
                if (JDBaseEntity.class.isAssignableFrom(type)) {
                    handleEntityParam(predicateList, orderList, criteriaBuilder, root.get(name), value);
                } else if (name.equals("params")) {
                    ((Map<String, Object>) value).forEach((key, value1) -> {
                        if (value1 != null && !String.valueOf(value1).isEmpty()) {
                            if (key.endsWith("Contains")) {
                                predicateList.add(criteriaBuilder.like(root.get(key.replace("Contains", "")), "%" + value1 + "%"));
                            } else if (key.endsWith("In")) {
                                List<Predicate> orConditions = new ArrayList<>();
                                String valueStr = String.valueOf(value1);

                                if (valueStr.contains(",")) {
                                    String[] split = valueStr.split(",");

                                    for (String s : split) {
                                        // 注意这里我们没有再次使用criteriaBuilder.or(...)
                                        orConditions.add(criteriaBuilder.like(criteriaBuilder.concat(criteriaBuilder.concat(",", root.get(key.replace("In", ""))), ","), "%," + s + ",%"));
                                    }
                                } else {
                                    orConditions.add(criteriaBuilder.like(criteriaBuilder.concat(criteriaBuilder.concat(",", root.get(key.replace("In", ""))), ","), "%," + valueStr + ",%"));
                                }

                                // 使用criteriaBuilder.or(...)将所有orConditions组合成一个单一的Predicate
                                Predicate orPredicate = criteriaBuilder.or(orConditions.toArray(new Predicate[0]));

                                // 将这个OR Predicate添加到predicateList中
                                predicateList.add(orPredicate);
                            } else if (key.endsWith("And")) {
                                String valueStr = String.valueOf(value1);
                                if (valueStr.contains(",")) {
                                    String[] split = valueStr.split(",");
                                    for (String s : split) {
                                        predicateList.add(criteriaBuilder.like(criteriaBuilder.concat(criteriaBuilder.concat(",", root.get(key.replace("And", ""))), ","), "%," + s + ",%"));
                                    }
                                } else {
                                    predicateList.add(criteriaBuilder.like(criteriaBuilder.concat(criteriaBuilder.concat(",", root.get(key.replace("And", ""))), ","), "%," + valueStr + ",%"));
                                }

                            } else if (key.endsWith("Equals")) {
                                predicateList.add(criteriaBuilder.equal(root.get(key.replace("Equals", "")), value1));
                            } else if (key.endsWith("GreaterThan")) {
                                if (key.contains("Time")) {
                                    predicateList.add(criteriaBuilder.greaterThan(root.get(key.replace("GreaterThan", "")), LocalDateTime.parse(String.valueOf(value1))));
                                } else {
                                    predicateList.add(criteriaBuilder.greaterThan(root.get(key.replace("GreaterThan", "")), String.valueOf(value1)));
                                }
                            } else if (key.endsWith("LessThan")) {
                                if (key.contains("Time")) {
                                    predicateList.add(criteriaBuilder.lessThan(root.get(key.replace("LessThan", "")), LocalDateTime.parse(String.valueOf(value1))));
                                } else {
                                    predicateList.add(criteriaBuilder.lessThan(root.get(key.replace("LessThan", "")), String.valueOf(value1)));
                                }
                            } else if (key.endsWith("Sort")) {
                                if (String.valueOf(value1).contains("desc")) {
                                    orderList.add(criteriaBuilder.desc(root.get(key.replace("Sort", ""))));
                                } else if (String.valueOf(value1).contains("asc")) {
                                    orderList.add(criteriaBuilder.asc(root.get(key.replace("Sort", ""))));
                                }
                            }
                        }
                    });
                }
            }
        });
    }

    public Optional<Entity> findById(Integer id) {
        return repository.findById(id);
    }

    public Optional<Entity> findByEntity(Entity entity) {
        return repository.findOne((Specification<Entity>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            List<Map<String, Object>> fieldList = FieldUtil.getFiledsInfo(entity);
            fieldList.stream().filter(field -> field.get("value") != null && !field.get("name").equals("createTime") && !field.get("name").equals("pageSize") && !field.get("name").equals("pageNumber") && !field.get("name").equals("params")).forEach(field -> {
                String name = field.get("name").toString();
                Object value = field.get("value");
                Class<?> type = (Class<?>) field.get("type");
                if (!Collection.class.isAssignableFrom(type) && !type.cast(value).toString().isEmpty()) {
                    predicateList.add(criteriaBuilder.equal(root.get(name), type.cast(value)));
                }

            });
            return criteriaQuery.where(predicateList.toArray(new Predicate[0])).getRestriction();
        });
    }

    public List<Entity> findAllByEntity(Entity entity) {
        return repository.findAll((Specification<Entity>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            List<Map<String, Object>> fieldList = FieldUtil.getFiledsInfo(entity);
            fieldList.stream().filter(field -> field.get("value") != null && !field.get("name").equals("createTime") && !field.get("name").equals("pageSize") && !field.get("name").equals("pageNumber") && !field.get("name").equals("params")).forEach(field -> {
                String name = field.get("name").toString();
                Object value = field.get("value");
                Class<?> type = (Class<?>) field.get("type");
                if (!Collection.class.isAssignableFrom(type) && !type.cast(value).toString().isEmpty()) {
                    predicateList.add(criteriaBuilder.equal(root.get(name), type.cast(value)));
                }

            });
            return criteriaQuery.where(predicateList.toArray(new Predicate[0])).getRestriction();
        });
    }
    public static String cleanData(String dataStr) {
        if (StringUtils.isBlank(dataStr)) {
            return dataStr;
        }
        // 使用正则表达式去除多余的逗号
        String cleanedStr = dataStr.replaceAll(",+", ",").trim(); // trim() 去除首尾的逗号

        // 分割字符串
        String[] items = cleanedStr.split(",");

        // 使用 HashSet 去重
        Set<String> uniqueItems = new LinkedHashSet<>(Arrays.asList(items));

        // 重新组合字符串
        StringBuilder sb = new StringBuilder();
        for (String item : uniqueItems) {
            if (sb.length() > 0) {
                sb.append(",");
            }
            sb.append(item);
        }

        return sb.toString();
    }
}
